<?php
/**
 * Nweb Framework
 *
 * This script is protected by copyright. It's use, copying, modification
 * and distribution without written consent of the author is prohibited.
 *
 * @category    source
 * @package     pl.nweb.framework.http
 * @subpackage  client.adapter
 * @author      Krzysztof Kardasz <krzysztof.kardasz@gmail.com>
 * @copyright   Copyright (c) 2011 Krzysztof Kardasz
 * @license     http://www.gnu.org/licenses/lgpl-3.0.txt  GNU Lesser General Public
 * @version     3.0 $Id: adapter.php 452 2011-12-07 12:04:19Z krzysztof.kardasz $
 * @link        http://code.google.com/p/nweb-framework/
 * @link        http://framework.nweb.pl
 */

namespace pl\nweb\framework\http\client;

/**
 * Adapter połączenia HTTP
 *
 * @category    source
 * @package     pl.nweb.framework.http
 * @subpackage  client.adapter
 * @author      Krzysztof Kardasz <krzysztof.kardasz@gmail.com>
 * @copyright   Copyright (c) 2011 Krzysztof Kardasz
 * @version     3.0 $Id: adapter.php 452 2011-12-07 12:04:19Z krzysztof.kardasz $
 */
abstract class Adapter
{
    /**
     * Wersja protokołu
     */
    const HTTP_1_0 = '1.0';
    const HTTP_1_1 = '1.1';

    /**
     * Metody
     */
    const METHOD_GET     = 'GET';
    const METHOD_POST    = 'POST';
    const METHOD_HEAD    = 'HEAD';
    const METHOD_PUT     = 'PUT';
    const METHOD_DELETE  = 'DELETE';
    const METHOD_TRACE   = 'TRACE';
    const METHOD_OPTIONS = 'OPTIONS';

    /**
     * Wersja protokołu
     *
     * @var string
     */
    protected $_httpVersion = self::HTTP_1_1;

    /**
     * Metoda
     *
     * @var string
     */
    protected $_method = self::METHOD_GET;

    /**
     * Uri
     *
     * @var string
     */
    protected $_uri = null;

    /**
     * Parametry GET
     *
     * @var array
     */
    protected $_get = array ();

    /**
     * Parametry POST
     *
     * @var array
     */
    protected $_post = array ();

    /**
     * Nagłówki
     *
     * @var array
     */
    protected $_headers = array ();

    /**
     * Body
     *
     * @var string
     */
    protected $_body = null;

    /**
     * Konfiguracja
     *
     * @var array
     */
    protected $_config = array ();

    /**
     * Obiekt odpowiedzi
     *
     * @var \pl\nweb\framework\http\Response
     */
    protected $_response = null;

    /**
     * Obiekt żądania
     *
     * @var \pl\nweb\framework\http\Request
     */
    protected $_request = null;

    /**
     * Definiuje konfigurację
     *
     * @param  array|string $key klucz/tablica wartości
     * @param  mixed $value wartość dla klucza
     * @return void
     */
    public function setConfig($key, $value = null)
    {
        if(is_array($key)) {
            $this->_config = $key;
        } else {
            $this->_config[$key] = $value;
        }
    }

    /**
     * Zwraca konfigurację / wartości konfiguracji
     *
     * @param  null|string $key klucz
     * @param  null|mixed $default domyślna wartość
     * @return void
     */
    public function getConfig($key = null, $default = null)
    {
        if (null === $key) {
            return $this->_config;
        }
        if (isset($this->_config[$key])) {
            return $this->_config[$key];
        }
        return $default;
    }

    /**
     * Definiuje metodę
     *
     * @param string $method metoda
     * @return void
     */
    public function setMethod($method)
    {
        $this->_method = $method;
    }

    /**
     * Definiuje adres url
     *
     * @param string $uri adres
     * @return void
     * @throws \pl\nweb\framework\http\client\adapter\Exception
     */
    public function setUri($uri)
    {
        $parts = parse_url($uri);
        if (empty($parts['scheme']) || empty($parts['host'])) {
            throw new adapter\Exception('Invalid Url', 1311, $this);
        }

        $url = $parts['scheme'].'://'.$parts['host'];
        if (!isset($parts['path'])) {
            $url .= '/';
        } else {
            $url .= $parts['path'];
        }

        $this->_uri = $url;
        if (isset($parts['query'])) {
            $this->_get += $parts['query'];
        }
    }

    /**
     * Definiuje treść żądania
     *
     * @param string $body treść
     * @return void
     */
    public function setBody($body)
    {
        $this->_body = (string)$body;
    }

    /**
     * Definiuje nagłówek
     *
     * @param string|array $name nazwa nagłówka lub tablica nagłówków
     * @param mixed|null $value wartość nagłówka
     * @return void
     */
    public function setHeader($name, $value = null)
    {
        if (is_array($name)) {
            foreach($name as $k => $v) {
                $this->setHeader($k, $v);
            }
        } else {
            $name = trim($name);
            $this->_headers[] = (null === $value) ? $name : ($name . ': ' . trim($value));
        }
    }

    /**
     * Definiuje parametr GET
     *
     * @param string|array $key klucz lub tablica parametrów
     * @param mixed|null $value wartość
     * @return void
     */
    public function setParamGet($key, $value = null)
    {
        if (is_array($key)) {
            $this->_get = array_merge($this->_get, $key);
        } else {
            $this->_get[$key] = $value;
        }
    }

    /**
     * Definiuje parametr POST
     *
     * @param string|array $key klucz lub tablica parametrów
     * @param mixed|null $value wartość
     * @return void
     */
    public function setParamPost($key, $value = null)
    {
        if (is_array($key)) {
            $this->_post = array_merge($this->_post, $key);
        } else {
            $this->_post[$key] = $value;
        }
    }

    /**
     * Wysyła żądanie, zwraca odpowiedź
     *
     * @param  string|null $method metoda
     * @return \pl\nweb\framework\http\Response
     * @throws \pl\nweb\framework\http\client\adapter\Exception
     */
    public function request($method = null)
    {
        if(null === $this->_uri) {
            throw new adapter\Exception ('Uri is not defined', 1312, $this);
        }

        if (null !== $method) {
            $this->setMethod($method);
        }

        if(count($this->_post) > 0) {
            $this->setBody(http_build_query($this->_post));
        }

        $this->_request = $this->_write();
        return $this->_response = $this->_read();
    }

    /**
     * Zwraca wysłane żądanie
     *
     * @return \pl\nweb\framework\http\Request
     * @throws \pl\nweb\framework\http\client\adapter\Exception
     */
    public function getRequest()
    {
        if(null == $this->_request) {
            throw new adapter\Exception ('Request not sent', 1313, $this);
        }
        return $this->_request;
    }

    /**
     * Zwraca otrzymaną odpowiedź
     *
     * @return \pl\nweb\framework\http\Response
     * @throws \pl\nweb\framework\http\client\adapter\Exception
     */
    public function getResponse()
    {
        if(null == $this->_response) {
            throw new adapter\Exception ('Request not sent', 1314, $this);
        }
        return $this->_response;
    }

    /**
     * Wysyła żądanie
     *
     * @return \pl\nweb\framework\http\Request
     * @throws \pl\nweb\framework\http\client\adapter\Exception
     */
    abstract protected function _write();

    /**
     * Odczytuje odpowiedź
     *
     * @return \pl\nweb\framework\http\Response
     * @throws \pl\nweb\framework\http\client\adapter\Exception
     */
    abstract protected function _read();
}